package com.horizon.im.common.codec;

import com.horizon.im.common.ProtocolInstant;
import com.horizon.im.common.bean.msg.ProtoMsg;
import com.horizon.im.common.exception.InvalidFrameException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

public class ProtoBufDecoder extends ByteToMessageDecoder {

    public ProtoBufDecoder() {
        super();
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //标记下当前readIndex的位置
        in.markReaderIndex();
        //判断包头长度
        if (in.readableBytes()<8) {
            return;
        }

        //读取魔数
        short magic = in.readShort();
        if (magic != ProtocolInstant.MAGIC_CODE) {
            String error = "客户端："+ctx.channel().remoteAddress()+"口令不对";
            throw new InvalidFrameException(error);
        }
        //读取版本
        short version = in.readShort();
        //读取传输过来的消息长度
        int length = in.readInt();
        // 长度如果小于0
        if (length < 0)
        {// 非法数据，关闭连接
            ctx.close();
        }

        if(length > in.readableBytes()) {
            //可读的消息小于length，表示分包了，需要等包到齐再读
            in.resetReaderIndex();
            return;
        }

        byte[] array;
        if (in.hasArray()) {
            //堆缓冲零拷贝读
            ByteBuf byteBuf = in.slice(in.readerIndex(), length);
            array = byteBuf.array();
            in.retain();//切片也需要计数
        } else {
            //直接缓冲区,由于业务需要用堆缓冲区，所以如果是直接缓冲区才需要拷贝
            array =new byte[length];
            in.readBytes(array);
        }
        //字节转换成对象
        ProtoMsg.Message outMsg =ProtoMsg.Message.parseFrom(array);
        //转换完成之后释放计数
        if (in.hasArray()) {
            in.release();
        } else {
            //非堆缓冲没有增加计数，因此不需要释放
        }

        if (outMsg!=null) {
            out.add(outMsg);
        }


















    }
}
